package application

import (
	"context"
	"errors"
	"time"

	"github.com/redis/go-redis/v9"
	"github.com/samber/do/v2"
	"github.com/spf13/viper"

	"gitee.com/monkeyPP/happy-mall/services/userservice/internal/domain/entity"
	"gitee.com/monkeyPP/happy-mall/services/userservice/internal/domain/service"
	"gitee.com/monkeyPP/happy-mall/services/userservice/internal/pkg/auth"
	"gitee.com/monkeyPP/happy-mall/services/userservice/internal/pkg/cache"
)

type UserApp interface {
	Register(ctx context.Context, user *entity.User) (*entity.User, error)
	Login(ctx context.Context, user *entity.User) (*auth.TokenPair, error)
	Logout(ctx context.Context, userID string) error
}

type UserAppImpl struct {
	userService service.UserService
	redisClient *redis.Client
}

func NewUserApp(i do.Injector) (UserApp, error) {
	userService := do.MustInvoke[service.UserService](i)
	redisClient := do.MustInvoke[*redis.Client](i)
	return &UserAppImpl{
		userService: userService,
		redisClient: redisClient,
	}, nil
}

func (u *UserAppImpl) Register(ctx context.Context, user *entity.User) (*entity.User, error) {
	// 密码加密
	password, err := auth.HashPassword(user.Password)
	if err != nil {
		return nil, err
	}
	user.Password = password
	user, err = u.userService.AddUser(ctx, user)
	if err != nil {
		return nil, err
	}
	return user, nil
}

func (u *UserAppImpl) Login(ctx context.Context, user *entity.User) (*auth.TokenPair, error) {
	// 通过 user_name 去数据库中查询用户密码
	dbUser, err := u.userService.GetUser(ctx, user)
	if err != nil {
		return nil, err
	}

	// 校验密码
	if isSame := auth.CheckPassword(user.Password, dbUser.Password); !isSame {
		return nil, errors.New("username or password error")
	}

	// 生成双 token
	tokenPair, err := auth.GenerateTokenPair(dbUser.ID)
	if err != nil {
		return nil, err
	}

	// 将 refreshToken 存到 redis
	expiryTime := viper.GetInt("jwt.refresh_expiry")
	cmd := u.redisClient.Set(ctx, cache.GenerateRefreshTokenKey(dbUser.ID), tokenPair.RefreshToken, time.Duration(expiryTime)*time.Second)
	if cmd.Err() != nil {
		return nil, cmd.Err()
	}
	return tokenPair, nil
}

func (u *UserAppImpl) Logout(ctx context.Context, userID string) error {
	delCmd := u.redisClient.Del(ctx, cache.GenerateRefreshTokenKey(userID))
	if delCmd.Err() != nil {
		return delCmd.Err()
	}
	return nil
}
